//安装：npm i @yume-chan/scrcpy (vue-admin-arco 用 pnpm)
//模板里的<canvas>只能通过style设置宽高，不能直接设置width/height属性

import type {ScrcpyMediaStreamDataPacket} from "@yume-chan/scrcpy";
import {h264ParseConfiguration} from "./h264";

// 转换数字为16进制字符串
function toHex(value: number) {
  const hex = value.toString(16).toUpperCase();
  return hex.length === 1 ? `0${hex}` : hex; // 手动补零
}

export class WebCodecsDecoder {
  public static isSupported() {
    return typeof globalThis.VideoDecoder !== "undefined";
  }

  private _spsHead:number[] = [];
  private _spsEnd:number[] = [];
  private _sps:number[] = [];
  private _spsReady = false;

  private _ppsHead:number[] = [];
  private _ppsEnd:number[] = [];
  private _pps:number[] = [];
  private _afterPPSData:number[] = [];
  private _ppsReady = false;

  private _codec: string;
  private _canvas: HTMLCanvasElement;
  private _frameRendered = 0;
  public get frameRendered() {
    return this._frameRendered;
  }

  private _frameSkipped = 0;
  public get frameSkipped() {
    return this._frameSkipped;
  }

  private context: CanvasRenderingContext2D;
  private decoder: VideoDecoder;
  private currentFrameRendered = false;
  private animationFrameId = 0;

  public constructor(canvas: HTMLCanvasElement, codec: string = 'h264') {
    this._codec = codec;
    this._canvas = canvas;
    this.context = this._canvas.getContext("2d")!;
    this._canvas.width = this._canvas.clientWidth;
    this._canvas.height = this._canvas.clientHeight;
    this.decoder = new VideoDecoder({
      output: (frame) => {
        if (this.currentFrameRendered) {
          this._frameSkipped += 1;
        } else {
          this.currentFrameRendered = true;
          this._frameRendered += 1;
        }

        // 将解码后的帧渲染到 canvas 上
        this.context.drawImage(frame, 0, 0, this._canvas.width, this._canvas.height);
        frame.close();
      },
      error(e) {
        console.error('Decoder error:', e);
      },
    });
    this.onFramePresented();
  }

  // 每帧渲染后，更新渲染状态
  private onFramePresented = () => {
    this.currentFrameRendered = false;
    this.animationFrameId = requestAnimationFrame(this.onFramePresented);
  };

  // 配置解码器
  public configure(spsAndPPS: Uint8Array) {
    switch (this._codec) {
      case "h264": {
        const {
          profileIndex,
          constraintSet,
          levelIndex,
          croppedWidth,
          croppedHeight,
        } = h264ParseConfiguration(spsAndPPS);
        console.log('分辨率：', croppedWidth + ' x ' + croppedHeight)
        const codec = `avc1.${[profileIndex, constraintSet, levelIndex].map(toHex).join("")}`;
        this.decoder.configure({codec, optimizeForLatency: true});
        break;
      }
    }
  }

  // 解码数据包
  public decode(packet: ScrcpyMediaStreamDataPacket) {
    let data = packet.data;

    if (this.decoder.state !== 'configured') {
      let spsFrame = this.findSPSFrame(packet.data)
      const {ppsFrame, afterPPSData} = this.findPPSFrame(packet.data)
      if (spsFrame == null) {
        return
      }
      if (ppsFrame != null) {
        let config = new Uint8Array(spsFrame.length + ppsFrame.length)
        config.set(spsFrame, 0)
        config.set(ppsFrame, spsFrame.length)
        this.configure(config);
        data = new Uint8Array(config.length + afterPPSData.length);
        data.set(config, 0);
        data.set(afterPPSData, config.length);
      } else {
        return;
      }
    }

    this.decoder.decode(
      new EncodedVideoChunk({
        type: packet.keyframe === false ? "delta" : "key",
        timestamp: 0, // 使用默认时间戳
        data,
      })
    );
    this.onFramePresented();
  }

  private findSPSFrame(data: Uint8Array): number[]|null {
    if (this._spsReady) {
      return this._sps
    }
    for (let i = 0; i < data.length; i++) {
      if (this._spsHead.length > 4) this._spsHead.shift()
      this._spsHead.push(data[i] as never)
      if (this._spsEnd.length > 3) this._spsEnd.shift()
      this._spsEnd.push(data[i] as never)
      if (!this._spsReady) {
        //找SPS帧
        if (this._sps.length == 0) {
          if (this._spsHead.length == 5 &&
            this._spsHead[0] === 0 && this._spsHead[1] === 0 && this._spsHead[2] === 0 && this._spsHead[3] === 1 && this._spsHead[4] === 103) {
            this._sps.push(...this._spsHead)
          }
        } else {
          this._sps.push(data[i] as never)
          //发现下一个头，停止加入并移除之前多加入的4个
          if (this._spsEnd[0] === 0 && this._spsEnd[1] === 0 && this._spsEnd[2] === 0 && this._spsEnd[3] === 1) {
            this._sps.splice(-4)
            this._spsReady = true
            // console.log('找到SPS帧', this._sps)
          }
        }
      }
    }
    return null
  }

  private findPPSFrame(data: Uint8Array) {
    if (this._ppsReady) {
      return {ppsFrame: this._pps, afterPPSData: this._afterPPSData}
    }
    for (let i = 0; i < data.length; i++) {
      if (this._ppsHead.length > 4) this._ppsHead.shift()
      this._ppsHead.push(data[i] as never)
      if (this._ppsEnd.length > 3) this._ppsEnd.shift()
      this._ppsEnd.push(data[i] as never)
      if (!this._ppsReady) {
        //找pps帧
        if (this._pps.length == 0) {
          if (this._ppsHead.length == 5) {
            if (this._ppsHead[0] === 0 && this._ppsHead[1] === 0 && this._ppsHead[2] === 0 && this._ppsHead[3] === 1 && this._ppsHead[4] === 104) {
              this._pps.push(...this._ppsHead)
            }
          }
        } else {
          this._pps.push(data[i] as never)
          //发现下一个头，停止加入并移除之前多加入的4个
          if (this._ppsEnd[0] === 0 && this._ppsEnd[1] === 0 && this._ppsEnd[2] === 0 && this._ppsEnd[3] === 1) {
            this._pps.splice(-4)
            this._ppsReady = true
            this._ppsEnd.pop()
            this._afterPPSData.push(...this._ppsEnd)
          }
        }
      }
      if (this._afterPPSData.length > 0) {
        this._afterPPSData.push(data[i] as never)
      }
    }
    return {ppsFrame: null, afterPPSData: null}
  }

  // 清理资源
  public dispose() {
    cancelAnimationFrame(this.animationFrameId);
    if (this.decoder.state !== "closed") {
      this.decoder.close();
    }
  }
}
